#pragma once

#include <cstdint>
#include <memory>
#include <string>
#include <utility>

#include "common/macros.h"
//#include "fmt/format.h"
#include "json/json.h"
#include "type/type.h"

class Column {
    friend class Schema;

   public:
    Column(){};
    /**
     * Non-variable-length constructor for creating a Column.
     * @param column_name name of the column
     * @param type type of the column
     * @param expr expression used to create this column
     */
    Column(std::string column_name, TypeId type)
        : column_name_(std::move(column_name)), column_type_(type), fixed_length_(TypeSize(type)) {
        DATABASE_ASSERT(type != TypeId::VARCHAR, "Wrong constructor for VARCHAR type.");
    }

    /**
     * Variable-length constructor for creating a Column.
     * @param column_name name of the column
     * @param type type of column
     * @param length length of the varlen
     * @param expr expression used to create this column
     */
    Column(std::string column_name, TypeId type, uint32_t length)
        : column_name_(std::move(column_name)),
          column_type_(type),
          fixed_length_(TypeSize(type)),
          variable_length_(length) {
        DATABASE_ASSERT(type == TypeId::VARCHAR, "Wrong constructor for non-VARCHAR type.");
    }

    /**
     * Replicate a Column with a different name.
     * @param column_name name of the column
     * @param column the original column
     */
    Column(std::string column_name, const Column &column)
        : column_name_(std::move(column_name)),
          column_type_(column.column_type_),
          fixed_length_(column.fixed_length_),
          variable_length_(column.variable_length_),
          column_offset_(column.column_offset_) {}

    /** @return column name */
    auto GetName() const -> std::string { return column_name_; }

    /** @return column length */
    auto GetLength() const -> uint32_t {
        if (IsInlined()) {
            return fixed_length_;
        }
        return variable_length_;
    }

    /** @return column fixed length */
    auto GetFixedLength() const -> uint32_t { return fixed_length_; }

    /** @return column variable length */
    auto GetVariableLength() const -> uint32_t { return variable_length_; }

    /** @return column's offset in the tuple */
    auto GetOffset() const -> uint32_t { return column_offset_; }

    /** @return column type */
    auto GetType() const -> TypeId { return column_type_; }

    /** @return true if column is inlined, false otherwise */
    auto IsInlined() const -> bool { return column_type_ != TypeId::VARCHAR; }

    /** @return a string representation of this column */
    std::string ToString() const;

    // 序列化
    void to_json(Json::Value &json_value) const;
    // 反序列化
    static bool from_json(const Json::Value &json_value, Column &column);

    // 初始化
    void init(std::string column_name, TypeId type, uint32_t offset);
    void init(std::string column_name, TypeId type, uint32_t length, uint32_t offset);

   private:
    /**
     * Return the size in bytes of the type.
     * @param type type whose size is to be determined
     * @return size in bytes
     */
    static auto TypeSize(TypeId type) -> uint8_t {
        switch (type) {
            case TypeId::BOOLEAN:
                return 1;
                return 2;
            case TypeId::INTEGER:
            case TypeId::FLOAT:
                return 4;
            case TypeId::LONG:
            case TypeId::DOUBLE:
            case TypeId::TIMESTAMP:
                return 8;
            case TypeId::VARCHAR:
                // TODO(Amadou): Confirm this.
                return 12;
            default: {
                UNREACHABLE("Cannot get size of invalid type");
            }
        }
    }

    /** Column name. */
    std::string column_name_;

    /** Column value's type. */
    TypeId column_type_;

    /** For a non-inlined column, this is the size of a pointer. Otherwise, the size of the fixed length column. */
    uint32_t fixed_length_;

    /** For an inlined column, 0. Otherwise, the length of the variable length column. */
    uint32_t variable_length_{0};

    /** Column offset in the tuple. */
    uint32_t column_offset_{0};
};